Edge Parser
Parser to convert edge template to invokable functions
Table of contents
This repo is the parser to convert edge templates to a self invoked Javascript function. Later you can invoke this function by providing a context.
Usage
Install the package from npm registry as follows:
npm i edge-parser
yarn add edge-parser
and then use it as follows
import { Parser } from 'edge-parser'
const tagsIfAny = {}
const parser = new Parser(tagsIfAny, { filename: 'foo.edge' })
parser.parse(`Hello {{ username }}`)
Output
let out = ''
let $lineNumber = 1
let $filename = 'eval.edge'
try {
out += 'Hello '
out += `${ctx.escape(state.username)}`
} catch (error) {
ctx.reThrow(error, $filename, $lineNumber)
}
return out
Notice of use of ctx
in the function body. Parser doesn't provide the implementation of ctx
, the runtime of template engine should provide it.
Parser API
Along with parsing the main template, the parser also exposes the API, that tags can use to selectively parse the content of a tag.
generateAST(jsExpression, lexerLoc, filename)
Parses a string as a Javascript expression. The output is a valid Estree expression
The following example returns a BinaryExpression
const loc = {
start: { line: 1, col: 1 },
end: { line: 1, col: 1 },
}
const filename = 'eval.edge'
parser.utils.generateAST('2 + 2', loc, filename)
transformAst(acornAst, filename)
Transform the acorn AST and make it compatible with Edge runtime. This method mutates the inner nodes of the original AST.
const loc = {
start: { line: 1, col: 1 },
end: { line: 1, col: 1 },
}
const filename = 'eval.edge'
parser.utils.transformAst(parser.utils.generateAST('2 + 2', loc, filename), filename)
tokenize (template)
Returns an array of lexer tokens for the given template. The method is a shortcut to self import the lexer module and then generating tokens.
const tokens = parser.tokenize('Hello {{ username }}')
Output
[
{
"type": "raw",
"line": 1,
"value": "Hello "
},
{
"type": "mustache",
"filename": "eval.edge",
"loc": {
"start": {
"line": 1,
"col": 8
},
"end": {
"line": 1,
"col": 20
}
},
"properties": {
"jsArg": " username "
}
}
]
stringify(expression)
Convert edge or acorn expression back to a string. This is helpful, when you mutate some nodes inside the expression and now want a valid Javascript string out of it.
const expression = parser.utils.generateAST(
'2 + 2',
{
start: { line: 1, col: 1 },
end: { line: 1, col: 1 },
},
'eval.edge'
)
expression.left.value = 3
parser.utils.stringify(expression)
parse(template)
Parse a template to an IIFE
. This is what you will use most of the time.
parser.parse('Hello {{ username }}')
Output
let out = ''
let $lineNumber = 1
let $filename = 'eval.edge'
try {
out += 'Hello '
out += `${ctx.escape(state.username)}`
} catch (error) {
ctx.reThrow(error, $filename, $lineNumber)
}
return out
processToken(token, buffer)
You will often find yourself using this method as a tag author, when you want to recursively process all children of your tag
const byPass = {
block: true,
seekable: false,
name: 'bypass',
compile(parser, buffer, token) {
token.children.forEach((child) => parser.processToken(child, buffer))
},
}
and then use it as
@bypass
Hello {{ username }}
@endbypass
Supported Expressions
The following expressions are supported by the parser. Can you also access the list of supported expressions as
import { expressions } from 'edge-parser'
Identifier
The identifier are prefixed with state.
In following statement username
is the identifier
Hello {{ username }}
Literal
A string literal
Hello {{ 'Guest' }}
ArrayExpression
The [1, 2, 3, 4]
is an array expression.
Evens are {{
[1, 2, 3, 4].filter((num) => num % 2 === 0)
}}
ObjectExpression
The { username: 'virk' }
is an Object expression
{{ toJSON({ username: 'virk' }) }}
UnaryExpression
Following are examples of UnaryExpression
.
{{ typeof(username) }}
{{ !!username }}
BinaryExpression
Here {{ 2 + 2 }}
is the binary expression
{{ 2 + 2 }} = 4
LogicalExpression
Following is the example of LogicalExpression
.
{{ username || admin.username }}
MemberExpression
{{ username.toUpperCase() }}
ConditionalExpression
{{ username ? username : 'Guest' }}
CallExpression
{{ upper(username) }}
SequenceExpression
Sequence is not supported in mustache blocks and instead used inside tags. For example:
Everything inside ()
is a sequence expression.
@component('button', text = 'Submit', type = 'Primary')
TemplateLiteral
{{ Hello `${username}` }}
ArrowFunctionExpression
{{
users.map((user) => {
return user.username
})
}}
AwaitExpression
{{ await foo() }}
FunctionDeclaration
{{ function foo () {} }}
Template expectations
You must define a context object with escape
and reThrow
methods when executing the parser compiled function
const ctx = {
escape(value) {
if (typeof value === 'string') {
return escapedValue
}
return value
},
reThrow(error, fileName, lineNumber) {},
}
API Docs
Following are the auto generated files via Type doc